/*
 * Created on 2005-3-11
 */
package com.hd123.rumba.sql;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import java.util.regex.Pattern;

import net.sf.hibernate.HibernateException;

import com.hd123.rumba.util.ObjectUtil;

/**
 * @author caili
 */
public class JdbcSelect extends SelectExecutorBase implements SelectExecutor {

  private int dbtype = ObjectUtil.dbtypeUnset;

  // private static String dialect = "net.sf.hibernate.dialect.Oracle9Dialect";

  /**
   * @param connection
   * @param query
   * @param pageSize
   *          &lt;=0 则结果不分页
   */
  public JdbcSelect(Connection connection, SQLSubquery query, int pageSize) {
    this.connection = connection;
    this.query = query;
    dbtype = ObjectUtil.getDbType(connection);
  }

  /**
   * 执行SELECT, 返回从start开始的size条记录. 如果size &lt;=0, 返回所有的行.(ORACLE语法)
   * 
   * @param
   * @return Iterator的元素是JDBCRow
   * @throws SQLException
   */
  public List getContentList(int start, int size) throws HibernateException {
    try {
      Date st = new Date();
      List list = new LinkedList();
      String statement = query.getSqlStatement();
      if (size > 0)
        statement = createPagedStatement(statement, start, size);
      exeStatement = statement;
      Vector parameters = query.getParametersVector();
      exeParameters = parameters;

      String perfConstruct = Long.toString(new Date().getTime() - st.getTime());
      st = new Date();

      PreparedStatement preparedStatement = connection.prepareStatement(statement);
      int i = 1;
      for (int k = 0; k < parameters.size(); ++k) {
        preparedStatement.setObject(i++, parameters.get(k));
      }
      if (size > 0) {
        if (dbtype == ObjectUtil.dbtypeOracle) {
          Integer lolimit = new Integer(start);
          Integer hilimit = new Integer(start + size);
          preparedStatement.setObject(i++, hilimit);
          exeParameters.add(hilimit);
          preparedStatement.setObject(i++, lolimit);
          exeParameters.add(Integer.valueOf(lolimit.intValue()));
        } else if (dbtype == ObjectUtil.dbtypeMySql) {
          Integer Start = new Integer(start);
          Integer Size = new Integer(size);
          preparedStatement.setObject(i++, Start);
          exeParameters.add(Start);
          preparedStatement.setObject(i++, Size);
          exeParameters.add(Size);
        }
      }

      logger.debug(exeStatement);
      logger.debug(exeParameters);
      String perfPrepare = Long.toString(new Date().getTime() - st.getTime());
      st = new Date();

      ResultSet resultset = null;
      try {
        resultset = preparedStatement.executeQuery();
        ResultSetMetaData resultsetmetadata = resultset.getMetaData();
        i = resultsetmetadata.getColumnCount();
        if (size > 0 && dbtype == ObjectUtil.dbtypeMsSql)
          while (resultset.getRow() == start - 1)
            resultset.next();
        while (resultset.next()) {
          JdbcRow jdbcrow = new JdbcRow();
          for (int k = 1; k <= i; k++) {
            String s = resultsetmetadata.getColumnName(k);
            Object obj = resultset.getObject(k);
            jdbcrow.put(s, obj);
          }
          list.add(jdbcrow);
        }
      } catch (SQLException sqlexception) {
        throw sqlexception;
      } finally {
        if (resultset != null)
          resultset.close();
        if (preparedStatement != null)
          preparedStatement.close();
      }
      String perfExecute = Long.toString(new Date().getTime() - st.getTime());
      logger.debug("select: " + perfConstruct + "|" + perfPrepare + "|" + perfExecute);

      return list;
    } catch (SQLException e) {
      throw new HibernateException(e);
    }
  }

  public Iterator getContentIterator(int start, int size) throws HibernateException {
    return getContentList(start, size).iterator();
  }

  protected String getCountStatement(SQLSubquery query) throws Exception {
    return query.getCountStatement();
  }

  private String createPagedStatement(String statement, int start, int size) {
    StringBuffer sb = new StringBuffer(3);
    if (dbtype == ObjectUtil.dbtypeOracle) {
      sb.append("select * from ( select row_.*, rownum rownum_ from ( ");
      sb.append(statement);
      sb.append(" ) row_ where rownum <= ?) where rownum_ > ?");
    } else if (dbtype == ObjectUtil.dbtypeMsSql) {
      String stm = Pattern.compile("^\\s*select\\s", Pattern.CASE_INSENSITIVE).matcher(statement)
          .replaceFirst("select top " + (start + size) + " ");
      sb.append(stm);
    } else {
      sb.append(statement);
      sb.append(" limit ?, ?");
    }
    return sb.toString();
  }
}
